package com.hxgis.common.service.impl;

import com.es.common.EsAggCondition;
import com.es.common.domain.EsCondition;
import com.es.wrapper.DataWrapper;
import com.hxgis.common.base.GroupAnalysisCache;
import com.hxgis.common.base.StationAssociated;
import com.hxgis.common.base.StationCache;
import com.hxgis.common.conare.NationArea;
import com.hxgis.common.conare.NationGroup;
import com.hxgis.common.conare.RainstormGroup;
import com.hxgis.common.dto.GroupAreaTargetDTO;
import com.hxgis.common.entity.RainstormInfo;
import com.hxgis.common.entity.Station;
import com.hxgis.common.repository.RainstormInfoRepository;
import com.hxgis.common.service.RainstormCourseService;
import com.hxgis.common.service.StationService;
import com.hxgis.common.utils.*;
import com.hxgis.common.utils.quickhull.QuickHull;
import com.hxgis.common.utils.quickhull.datastructures.LinkedList;
import com.hxgis.common.vo.GroupAnalysisVO;
import com.hxgis.common.vo.StationVO;
import lombok.extern.slf4j.Slf4j;
import org.geojson.FeatureCollection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.*;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
 * @author cqj
 * @version 1.0
 * @date 2019/9/23 8:51
 */
@Service
@Slf4j
public class RainstormCourseServiceImpl implements RainstormCourseService {

    public static final Integer MINCOUNTNUM = 20;


    @Autowired
    RainstormInfoRepository rainstormInfoRepository;

    @Autowired
    StationService stationService;


    /**
     * 全国范围内的区域性暴雨站组
     * @param startTime
     * @param endTime
     */
    @Override
    public void countRainStormGroup(String startTime, String endTime, List<String> stationNos) {
        //根据时间分组查询出所有的暴雨数据 进行计算暴雨站组
        List<RainstormInfo> rainstormInfos = selectList(startTime,endTime,CollectionUtils.convertCollectionString(stationNos));
        //根据时间分组
        Map<Long, List<RainstormInfo>> collect = rainstormInfos.stream().collect(Collectors.groupingBy(RainstormInfo::getTime));
        List<RainstormGroup> totalRainstromGroups = new ArrayList<>();

        collect.forEach((time,v) -> {
            //第一步必须大于20
            if (v.size() >= MINCOUNTNUM){
                List<RainstormGroup> rainstormGroupAreas = new ArrayList<>();
//                List<Set<RainstormInfo>> setList = new ArrayList<>();
                Map<String,Set<RainstormInfo>> areaMap = new HashMap<String, Set<RainstormInfo>>();
                for (RainstormInfo rainstormInfo : v) {
                    if (!handler(rainstormInfo,areaMap)){
                        Set<RainstormInfo> rainstormInfoSet = new HashSet<>();
                        rainstormInfoSet.add(rainstormInfo);
                        areaMap.put(UUID.randomUUID().toString().replace("-",""),rainstormInfoSet);
                    }
                }
                areaMap.forEach((key,value) -> {
                    RainstormGroup rainstormGroup = new RainstormGroup();
                    rainstormGroup.setAreaFlag(key);
                    rainstormGroup.setRainstormInfos(value);
                    rainstormGroupAreas.add(rainstormGroup);
                });
                //把站点集合小于20的remove调
                Iterator<RainstormGroup> iterator = rainstormGroupAreas.iterator();
                while (iterator.hasNext()){
                    if (iterator.next().getRainstormInfos().size() < MINCOUNTNUM){
                        iterator.remove();
                    }
                }
                totalRainstromGroups.addAll(rainstormGroupAreas);
            }
        });
        log.info("时间{}到{}的暴雨站组有{}个",startTime,endTime,totalRainstromGroups.size());
        List<RainstormInfo> rainstormInfoList = transform(totalRainstromGroups);
        rainstormInfoRepository.saveBatch(rainstormInfoList);
    }



    private boolean handler(RainstormInfo rainstormInfo, Map<String, Set<RainstormInfo>> areaMap){
        List<String> mergeAreaFlag = new ArrayList<String>();
        for (String areaFlag:areaMap.keySet()){
            Set<RainstormInfo> rainstormInfoSet = areaMap.get(areaFlag);
            // 先进行条件判断
            if (checkArea(rainstormInfoSet,rainstormInfo)){
                mergeAreaFlag.add(areaFlag);
            }
        }
        if (org.springframework.util.CollectionUtils.isEmpty(mergeAreaFlag)){
            return false;
        }
        Set<RainstormInfo> rainstormInfos = mergeArea(areaMap, mergeAreaFlag);
        rainstormInfos.add(rainstormInfo);
        return true;
    }

    private Set<RainstormInfo> mergeArea(Map<String,Set<RainstormInfo>> areaMap,List<String> mergeAreaFlagList){
        Set<RainstormInfo> rainstormInfoSet = areaMap.get(mergeAreaFlagList.get(0));
        for (int i = 1; i < mergeAreaFlagList.size(); i++) {
            String areaFlag = mergeAreaFlagList.get(i);
            rainstormInfoSet.addAll(areaMap.get(areaFlag));
            areaMap.remove(areaFlag);
        }
        return rainstormInfoSet;
    }


    private boolean checkIntersection(Set<RainstormInfo> rainstormInfoSet1, Set<RainstormInfo> rainstormInfoSet) {
        Set<RainstormInfo> resultSet = new HashSet<>();
        resultSet.addAll(rainstormInfoSet1);
        resultSet.retainAll(rainstormInfoSet);
        if (org.apache.commons.collections4.CollectionUtils.isEmpty(resultSet)){
            return false;
        }
        return true;
    }


    /**
     * 全国范围内的区域性暴雨过程
     * @param startTime
     * @param endTime
     */
    @Override
    public void countRainStormAreaGroup(String startTime, String endTime) {
        List<RainstormInfo> rainstormGroup = getRainstormGroup(startTime, endTime);
        //根据组标识分组，分成不同的组
        Map<String, List<RainstormInfo>> groupList = rainstormGroup.stream().collect(Collectors.groupingBy(RainstormInfo::getNationAreaGroup));
        //转换成组对象
        List<NationGroup> nationGroups = converNationGroup(groupList);
        nationGroups.sort(((o1, o2) -> {
            return (int)(o1.getTime() - o2.getTime());
        }));
        //转换成组过程
        Collection<NationArea> nationAreas = coverNationArea(nationGroups);
        if (CollectionUtils.isNotBlank(nationAreas)){
            List<RainstormInfo> rainstormInfoList = new ArrayList<RainstormInfo>();
            for (NationArea nationArea : nationAreas) {
                rainstormInfoList.addAll(getRainstormInfoList(nationArea));
            }
            rainstormInfoRepository.saveBatch(rainstormInfoList);
        }
        log.info("{}到{},全国范围内的区域性暴雨过程数据保存完成",startTime,endTime);
    }



    @Override
    public List<GroupAreaTargetDTO> analysisGroup(String startTime, String endTime) {
        List<GroupAreaTargetDTO> groupAreaTargetDTOS = new ArrayList<>();
        List<RainstormInfo> rainGroupByCourseArea = getRainGroupByCourseArea(startTime, endTime);
        if (!org.springframework.util.CollectionUtils.isEmpty(rainGroupByCourseArea)){
            //根据组过程标识分组，分成不同的组
            Map<String, List<RainstormInfo>> groupAreas = rainGroupByCourseArea.stream().collect(Collectors.groupingBy(RainstormInfo::getCourseContinue));
            groupAreas.forEach((key,value) -> {
                GroupAreaTargetDTO groupAreaTargetDTO = new GroupAreaTargetDTO(value);
                groupAreaTargetDTOS.add(groupAreaTargetDTO);
            });
        }
        return groupAreaTargetDTOS;
    }

    /**
     * 计算暴雨组过程指标
     * @param startTime
     * @param endTime
     */
    @Override
    public List<GroupAnalysisVO> analysisGroupIndex(String startTime, String endTime) {
        List<RainstormInfo> rainGroupByCourseArea = getRainGroupByCourseArea(startTime, endTime);
        if (org.springframework.util.CollectionUtils.isEmpty(rainGroupByCourseArea)){
            return new ArrayList<>();
        }
        Map<String, Station> stringStationMap = StationCache.getStationInfoMap();
        Map<String, LinkedList> map = stationService.queryAllStationAffect();
        //根据组过程标识分组，分成不同的组
        Map<String, List<RainstormInfo>> groupAreas = rainGroupByCourseArea.stream().collect(Collectors.groupingBy(RainstormInfo::getCourseContinue));
        List<GroupAreaTargetDTO> groupAreaTargetDTOS = new ArrayList<>();
        groupAreas.forEach((key,value) -> {
            GroupAreaTargetDTO groupAreaTargetDTO = new GroupAreaTargetDTO(value);
            groupAreaTargetDTOS.add(groupAreaTargetDTO);
        });
        //排序 方便前端显示
        groupAreaTargetDTOS.sort(((o1, o2) -> {
            return (int)(o2.getStartTime() - o1.getEndTime());
        }));
        List<GroupAnalysisVO> groupAnalysisVOS = new ArrayList<>();
        for (int i = 0; i < groupAreaTargetDTOS.size(); i++) {
            GroupAreaTargetDTO groupAreaTargetDTO = groupAreaTargetDTOS.get(i);
            GroupAnalysisVO groupAnalysisVO = new GroupAnalysisVO();
            groupAnalysisVO.setRange(i + 1);
            groupAnalysisVO.setEndTime(groupAreaTargetDTO.getEndTime());
            groupAnalysisVO.setAvgStrength(groupAreaTargetDTO.getAvgStrength());
            groupAnalysisVO.setStartTime(groupAreaTargetDTO.getStartTime());
            groupAnalysisVO.setCompositeIndex(groupAreaTargetDTO.getCompositeIndex());
            groupAnalysisVO.setContinueDays(groupAreaTargetDTO.getContinueDays());
            groupAnalysisVO.setGroupAreaFlag(groupAreaTargetDTO.getGroupAreaFlag());
            groupAnalysisVO.setGetAffectArea(groupAreaTargetDTO.getGetAffectArea());

            //这个区域的所有站点信息
            List<RainstormInfo> rainstormInfos = groupAreas.get(groupAreaTargetDTO.getGroupAreaFlag());
            //计算边界
            QuickHull quickHull = new QuickHull();
            com.hxgis.common.utils.quickhull.datastructures.LinkedList pointSet = new com.hxgis.common.utils.quickhull.datastructures.LinkedList();
            String type = "";
            Set<StationVO> stationVOHashSet = new HashSet<>();
            rainstormInfos.stream().forEach(x -> {
                pointSet.insertAll(map.get(x.getStationNo()));
                StationVO stationVO = new StationVO();
                stationVO.setLat(Double.valueOf(stringStationMap.get(x.getStationNo()).getLat()));
                stationVO.setLon(Double.valueOf(stringStationMap.get(x.getStationNo()).getLon()));
                stationVO.setStationNo(x.getStationNo());
                stationVOHashSet.add(stationVO);
            });
            LinkedList calcPointsSet = quickHull.useAlgorithm(pointSet);
            FeatureCollection draw = GeoUtil.draw(calcPointsSet, groupAreaTargetDTO.getGroupAreaFlag());
            groupAnalysisVO.setFeatureCollection(draw);
            groupAnalysisVO.setStationVOS(stationVOHashSet);
            //计算强度指标
            List<Double> groupStandards = GroupAnalysisCache.getGroupStandards();
            Predicate<Double> scorteFiter = (f) -> (f.doubleValue() >= groupAreaTargetDTO.getCompositeIndex());
            List<Double> collect = groupStandards.stream().filter(scorteFiter).collect(Collectors.toList());
            double v = NumUtil.save2Point((collect.size() * 1.0 / groupStandards.size()) * 100);
            groupAnalysisVO.setCompositeIndexPercent(v);
            groupAnalysisVO.setCompositeIndexFlag(GroupAreaUtil.getAnalysisIndex(v / 100));
            groupAnalysisVOS.add(groupAnalysisVO);
        }
        return groupAnalysisVOS;
    }


    /**
     * 全国暴雨组过程接口
     * @param groupAreaFlag
     * @return
     */
    @Override
    public List groupAnimationTarget(String groupAreaFlag) {
        return null;
    }

    private List<RainstormInfo> getRainGroupByCourseArea(String startTime, String endTime) {
        EsAggCondition esAggCondition = EsAggCondition.getInstance();
        esAggCondition.Group("courseContinue").size(100000);
        esAggCondition.getCondition().gte("time",Long.valueOf(startTime)).lte( Long.valueOf(endTime))
                .eq("courseContinue",null).not();

        List<String> areaGroups = rainstormInfoRepository.selectListAgg(esAggCondition, new DataWrapper<String>() {
            @Override
            public String wrapper(Map<String, Object> data) {
                return (String) data.get("courseContinue");
            }
        });

        if (org.springframework.util.CollectionUtils.isEmpty(areaGroups)){
            return new ArrayList<RainstormInfo>();
        }
        EsCondition groupCondition = EsCondition.getEsCondition().in("courseContinue", areaGroups).size(1000000);
        List<RainstormInfo> rainstormInfos = rainstormInfoRepository.selectList(groupCondition);
        return rainstormInfos;
    }

    private List<RainstormInfo> getRainstormInfoList(NationArea nationArea){
        initAraFlag(nationArea);
        List<RainstormInfo> rainstormInfos = new ArrayList<RainstormInfo>();
        for (NationGroup nationGroup : nationArea.getNationGroups()) {
            for (RainstormInfo rainstormInfo : nationGroup.getRainstormInfos()) {
                rainstormInfo.setCourseContinue(nationArea.getNationFlag());
                rainstormInfos.add(rainstormInfo);
            }
        }
        return rainstormInfos;
    }

    /**
     * 这里处理跨年的情况
     * @param nationArea
     */
    private void initAraFlag(NationArea nationArea){
        final String[] nationFlag = {nationArea.getNationFlag()};
        Set<NationGroup> nationGroups = nationArea.getNationGroups();
        List<NationGroup> nationGroupList = new ArrayList<NationGroup>(nationGroups);
        nationGroupList.sort(((o1, o2) -> {
            return (int)(o1.getTime() - o2.getTime());
        }));
        //用第一个时间去找 courseContinue 找到了用上一个的
        String preDay = TimeUtil.preDay(nationGroupList.get(0).getTime().toString(), 1, TimeUtil.FORMATOR_YMD);
        EsCondition rainStormCondition = EsCondition.getEsCondition();
        rainStormCondition.eq("time",Long.parseLong(preDay))
                .eq("courseContinue",null).not();
        rainStormCondition.size(1000000);
        List<RainstormInfo> rainstormInfos = rainstormInfoRepository.selectList(rainStormCondition);
        //前一天找到了
        if (!org.springframework.util.CollectionUtils.isEmpty(rainstormInfos)){
            //在比较中心距离
            Map<String, List<RainstormInfo>> groupList = rainstormInfos.stream().collect(Collectors.groupingBy(RainstormInfo::getNationAreaGroup));
            List<NationGroup> nationGroupList1 = converNationGroup(groupList);
            //有可能有两个组
            nationGroupList1.stream().forEach(x -> {
                NationGroup nationGroup = this.checkArea(nationGroupList1, x);
                if (!org.springframework.util.CollectionUtils.isEmpty(rainstormInfos)){
                    nationFlag[0] = nationGroup.getRainstormInfos().get(0).getCourseContinue();
                }
            });
        }


//        for (NationGroup nationGroup : nationArea.getNationGroups()) {
//            RainstormInfo rainstormInfo = nationGroup.getRainstormInfos().get(0);
//            String nationCourse = rainstormInfo.getCourseContinue();
//            if (nationCourse != null) nationFlag = nationCourse;
//        }


        nationArea.setNationFlag(nationFlag[0]);
        return;
    }

    private NationGroup checkArea(List<NationGroup> nationGroups, NationGroup nationGroup) {
        for (NationGroup nationGroup1 : nationGroups) {
            double v = DistanceUtil.countDistance(nationGroup1.getCenterPoint()[1], nationGroup1.getCenterPoint()[0],
                    nationGroup.getCenterPoint()[1], nationGroup.getCenterPoint()[0]);
            BigDecimal bigDecimal = new BigDecimal(v);
            if (bigDecimal.compareTo(new BigDecimal(1050)) <=0){
                return nationGroup1;
            }
        }
        return null;
    }



    private Collection<NationArea> coverNationArea(List<NationGroup> nationGroups) {
        // 区域过程  key: 组过程标识符号  value: 暴雨组过程
        Map<String,NationArea> areaMap = new HashMap<String, NationArea>();
        for (NationGroup nationGroup : nationGroups) {
            if (!handlerRainstorm(nationGroup,areaMap)){
                NationArea nationArea = new NationArea(nationGroup);
                areaMap.put(nationArea.getNationFlag(),nationArea);
            }
        }
        List<NationArea> nationAreas = new ArrayList<NationArea>();
        areaMap.forEach((s, nationArea) -> {
            if (nationArea.getNationGroups().size()>=2){
                nationAreas.add(nationArea);
            }
        });
        return nationAreas;
    }


    /**
     * 暴雨组过程处理
     * @param nationGroup
     * @param areaMap
     * @return true 找到区域  false 没有找到区域
     */
    private boolean handlerRainstorm(NationGroup nationGroup,Map<String,NationArea> areaMap){
        List<String> mergeAreaFlag = new ArrayList<String>();
        for (String areaFlag:areaMap.keySet()){
            NationArea nationArea = areaMap.get(areaFlag);
            // 先进行条件判断
            if (nationArea.check(nationGroup)){
                mergeAreaFlag.add(areaFlag);
            }
        }
        //找不到暴雨处理过程 返回false
        if (org.springframework.util.CollectionUtils.isEmpty(mergeAreaFlag)){
            return false;
        }
        NationArea nationArea = mergeAreaProcess(areaMap, mergeAreaFlag);
        nationArea.add(nationGroup);
        //找到了暴雨过程
        return true;
    }

    /**
     * 合并区域过程
     */
    private NationArea mergeAreaProcess(Map<String,NationArea> areaMap,List<String> mergeAreaFlagList){
        NationArea nationArea = areaMap.get(mergeAreaFlagList.get(0));
        for (int i = 1; i < mergeAreaFlagList.size(); i++) {
            String areaFlag = mergeAreaFlagList.get(i);
            nationArea.merge(areaMap.get(areaFlag));
            areaMap.remove(areaFlag);
        }
        return nationArea;
    }

    private List<NationGroup> converNationGroup(Map<String, List<RainstormInfo>> groupList) {
        List<NationGroup> nationGroups = new ArrayList<>();
        groupList.forEach((groupFlag,value) -> {
            NationGroup nationGroup = new NationGroup();
            nationGroup.setTime(value.get(0).getTime());
            nationGroup.setRainstormInfos(value);
            //中心点
            nationGroup.setCenterPoint(stationService.getCenter(value));
            nationGroups.add(nationGroup);
        });
        return nationGroups;
    }


    /**
     * 根据antionAreaGroup分组求出
     * @param startTime
     * @param endTime
     */
    private List<RainstormInfo> getRainstormGroup(String startTime, String endTime) {
        EsAggCondition esAggCondition = EsAggCondition.getInstance();
        esAggCondition.Group("nationAreaGroup").size(100000);


        esAggCondition.getEsCondition()
                .GTE("time", Long.valueOf(startTime))
                .LTE("time", Long.valueOf(endTime))
                .eq("nationAreaGroup",null).not();
        List<String> areaGroups = rainstormInfoRepository.selectListAgg(esAggCondition, new DataWrapper<String>() {
            @Override
            public String wrapper(Map<String, Object> data) {
                return (String) data.get("nationAreaGroup");
            }
        });
        EsCondition groupCondition = EsCondition.getEsCondition();
        groupCondition.in("nationAreaGroup", CollectionUtils.convertCollectionString(areaGroups));
        groupCondition.size(1000000);
        return rainstormInfoRepository.selectList(groupCondition);
    }

    private List<RainstormInfo> transform(List<RainstormGroup> totalRainstromGroups){
        List<RainstormInfo> rainstormInfos = new ArrayList<>();
        if (!org.springframework.util.CollectionUtils.isEmpty(totalRainstromGroups)){
            totalRainstromGroups.stream().forEach(x -> {
                x.getRainstormInfos().stream().forEach(y -> {
                    y.setNationAreaGroup(x.getAreaFlag());
                    rainstormInfos.add(y);
                });
            });
        }
        return rainstormInfos;
    }


    private Map<String, Set<RainstormInfo>> getAreaMap(Map<String, Set<RainstormInfo>> areaMap,RainstormInfo rainstormInfo){
        LinkedHashMap<String, Set<RainstormInfo>> returnMap = new LinkedHashMap<>();
        if (org.springframework.util.CollectionUtils.isEmpty(areaMap)){
            Set<RainstormInfo> set = new HashSet<>();
            //生成区域标识
            String areaFlag = UUID.randomUUID().toString().replace("-","");
            set.add(rainstormInfo);
            returnMap.put(areaFlag,set);
        }else{
            Set<String> strings = areaMap.keySet();
            Iterator<String> iterator = strings.iterator();
            while(iterator.hasNext()) {
                returnMap.putAll(areaMap);
                String key = iterator.next();
                Set<RainstormInfo> value = areaMap.get(key);
                if (checkArea(value,rainstormInfo)){
                    value.add(rainstormInfo);
                    returnMap.put(key,value);
//                    returnMap.putAll(areaMap);
                }else{
                    Set<RainstormInfo> set = new HashSet<>();
                    //生成区域标识
                    String areaFlag = UUID.randomUUID().toString().replace("-","");
                    set.add(rainstormInfo);
                    //ConcurrentModificationException
                    returnMap.put(areaFlag,set);
                }
            }
        }
        return returnMap;
    }

    /**
     * 根据时间分组查询出所有的暴雨数据 进行计算暴雨站组
     * @param startTime
     * @param endTime
     * @param stationNos
     * @return
     */
    public List<RainstormInfo> selectList(String startTime, String endTime, String ... stationNos){

        EsAggCondition esAggCondition = EsAggCondition.getInstance();
        esAggCondition.Group("time").size(100000);
        esAggCondition.getEsCondition()
                .GTE("time", Long.valueOf(startTime))
                .LTE("time", Long.valueOf(endTime))
                .in("stationNo", stationNos)
                .GTE("rainFall",500)
                .LTE("rainFall",20000);
        List<String> times = rainstormInfoRepository.selectListAgg(esAggCondition, new DataWrapper<String>() {
            @Override
            public String wrapper(Map<String, Object> data) {
                return data.get("time").toString();
            }
        });
        EsCondition timeCondition = EsCondition.getEsCondition();
        timeCondition.in("time",CollectionUtils.convertCollectionString(times));
        timeCondition.size(1000000);
        return rainstormInfoRepository.selectList(timeCondition);
    }


    /**
     * 校验区域是否存在交集
     * @param rainstormInfos
     * @param rainstormInfo
     * @return
     */
    private boolean checkArea(Set<RainstormInfo> rainstormInfos,RainstormInfo rainstormInfo){
        StationAssociated stationAssociated =
                StationCache.getScopeStation350List(rainstormInfo.getStationNo());
        for (RainstormInfo rainstormInfo1 : rainstormInfos) {
            Station associated = stationAssociated.getStationAssociated(rainstormInfo1.getStationNo());
            if (associated != null){
                return true;
            }
        }
        return false;
    }


}
